package com.eraasoftware.npa.server;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.NoSuchElementException;

import com.eraasoftware.npa.client.ActionHandler;
import com.eraasoftware.npa.client.Header;
import com.eraasoftware.npa.client.Header.ActionEnum;
import com.eraasoftware.npa.client.ObjectResponse;
import com.eraasoftware.npa.client.RespHeader.Status;
import com.eraasoftware.npa.io.ChannelBuffer;
import com.eraasoftware.npa.io.Serializer;

/**
 * Implement this class for remote call<br>
 * Usage : <br>
 * 
 * <pre>
 * ObjectActionHandler&lt;String&gt; messageHandler = new ObjectActionHandler&lt;String&gt;() {
 * 
 * 	public String objectResponse(Header header, ChannelBuffer channel) throws IOException {
 * 		return &quot;hello&quot;;
 * 	}
 * };
 * String message = messageHandler.invoke();
 * 
 * </pre>
 * 
 * @author eddie
 * 
 * @param <U>
 */

public abstract class ObjectActionHandler<U> implements ActionHandler {
	Header head;
	ByteBuffer buffer;

	public ObjectActionHandler<U> buffer(ByteBuffer buffer) {
		this.buffer = buffer;
		return this;
	}

	public ObjectActionHandler<U> head(Header head) {
		this.head = head;
		return this;
	}

	public Header head() {
		if (head == null)
			head = new Header();
		return head;
	}

	public ByteBuffer buffer() {
		if (buffer == null)
			buffer = ByteBuffer.allocate(500);
		return buffer;
	}

	@Override
	public final void received(Header header, ChannelBuffer channel) throws IOException {
		U resp = objectResponse(header, channel);

		if (resp != null) {
			Serializer.instance().writeObject(buffer(), resp);
		}
		buffer().flip();
		channel.write(buffer());

	}

	public abstract U objectResponse(Header header, ChannelBuffer channel) throws IOException;

	public ObjectResponse<U> invoke() {
		ObjectResponse<U> objResp = new ObjectResponse<U>();

		head();
		buffer().clear();
		head.setAction(ActionEnum.NATIVE);
		head.setActionHandler(this.getClass().getName());
		Serializer.instance().writeObject(buffer, head);
		ServiceListener service = null;
		try {
			service = ServiceListenerPoolFactory.instance().getService();
			buffer.flip();
			if (service != null) {
				byte[] rep = service.sendQuestion(buffer);
				ByteBuffer repBuff = ByteBuffer.wrap(rep);
				// repBuff.flip();
				objResp.setValue(Serializer.instance().readObject(repBuff, getTypeParameterClass()));
				objResp.header().setStatus(Status.OK);

			} else {
				String errmess = "no service available";

				throw new NoSuchElementException(errmess);
			}

		} catch (NoSuchElementException e) {
			handleError(e, objResp, Status.NO_SERVICE_AVAILABLE);
		} catch (IllegalStateException e) {
			handleError(e, objResp, Status.ILLEGAL_STATE);
		} catch (InterruptedException e) {
			handleError(e, objResp, Status.INTERUPTED);
		} catch (IOException e) {
			handleError(e, objResp, Status.IO_ERR);
		} finally {
			ServiceListenerPoolFactory.instance().returnObject(service);
		}

		return objResp;
	}

	private void handleError(Exception e, ObjectResponse<U> objResp, Status status) {
		objResp.header().setStatus(status);
		objResp.header().setMessage(e.getMessage());

	}

	@SuppressWarnings("unchecked")
	public Class<U> getTypeParameterClass() {
		Type type = getClass().getGenericSuperclass();
		ParameterizedType paramType = (ParameterizedType) type;
		return (Class<U>) paramType.getActualTypeArguments()[0];
	}

}
